package com.thx.common.web.filter;

import com.thx.common.web.WebConstant;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * 对需要登录后才能访问的 url 进行检查.
 *
 * @author 贾红磊
 */
public abstract class SessionFilter implements Filter {

  private static Logger log = LoggerFactory.getLogger(SessionFilter.class);

  private static String[] excludePaths;// 免检路径
  private static String[] excludePathRegs;// 免检路径正则

  private static String failGo;// 检查不通过的跳转路径

  private static String welcome;// 登录成功后的首界面

  private static String refuse;// 拒绝操作，如没有权限时返回的界面

  private static String baseUrl;

  /**
   * 初始化 Servlet, 加载配置.
   */
  public void init(FilterConfig config) throws ServletException {
    String exclude = config.getInitParameter("excludePaths");
    if (exclude != null && !exclude.trim().equals("")) {
      excludePaths = exclude.split(",");
    }
    String excludeReg = config.getInitParameter("excludePathRegs");
    if (StringUtils.isNotBlank(excludeReg)) {
      excludePathRegs = excludeReg.split(",");
    }

    failGo = config.getInitParameter("failGo");
    if (failGo == null) {
      failGo = "failGo.html";
    }

    welcome = config.getInitParameter("welcome");
    if (welcome == null) {
      welcome = "welcome.html";
    }

    refuse = config.getInitParameter("refuse");
    if (refuse == null) {
      refuse = "refuse.html";
    }


    ServletContext context = config.getServletContext();
    baseUrl = context.getInitParameter("baseUrl");
  }

  /**
   * 过滤方法.
   */
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {

    HttpServletRequest httpRequest = (HttpServletRequest) request;

    HttpServletResponse httpResponse = (HttpServletResponse) response;

    httpRequest.setAttribute(WebConstant.ACCESS_TYPE, "pc端");

    // Forces caches to obtain a new copy of the page from the origin server
    httpResponse.setHeader("Cache-Control", "no-cache");
    // Directs caches not to store the page under any circumstance
    httpResponse.setHeader("Cache-Control", "no-store");
    httpResponse.setDateHeader("Expires", 0); // Causes the proxy cache to see the page as "stale"
    httpResponse.setHeader("Pragma", "no-cache");

    String requestPath = httpRequest.getRequestURI();
    String servletPath = httpRequest.getServletPath();

    log.debug("servletPath:" + servletPath + " requestPath:" + requestPath);

    if (!isExcludable(servletPath)) {

      HttpSession session = httpRequest.getSession();

      if (checkSession(session, httpRequest, httpResponse)) {

        if (checkPrivilege(session, httpRequest, httpResponse)) {
          chain.doFilter(request, response);
        } else {
          noPrivilege(session, httpRequest, httpResponse);
        }

      } else {
        noSession(session, httpRequest, httpResponse);
      }

    } else {
      if (servletPath.contains("login.jsp") || servletPath.contains("login!input.action")) {
        HttpSession session = httpRequest.getSession();
        if (session != null && checkSession(session, httpRequest, httpResponse)) {
          httpResponse.sendRedirect(baseUrl + "/" + welcome);
        }
      }
      chain.doFilter(request, response);
    }
  }

  private void noSession(HttpSession session, HttpServletRequest httpRequest,
      HttpServletResponse httpResponse) throws IOException {
    if (isAjaxRequest(httpRequest)) {
      setAjaxResp(httpResponse, "timeout", "会话超时", 900);
    } else {
      httpResponse.sendRedirect(baseUrl + "/" + failGo);
    }
  }

  private void noPrivilege(HttpSession session, HttpServletRequest httpRequest,
      HttpServletResponse httpResponse) throws IOException {

    if (isAjaxRequest(httpRequest)) {
      setAjaxResp(httpResponse, "noauth", "没有操作权限!", 910);
    } else {
      httpResponse.sendRedirect(baseUrl + "/" + refuse);
    }
  }

  private void setAjaxResp(HttpServletResponse httpResponse, String code, String desc,
      int httpStatus) throws IOException {
    httpResponse.setContentType("text/html;charset=UTF-8");
    httpResponse.setCharacterEncoding("utf-8");
    httpResponse.setHeader("Charset", "utf-8");
    httpResponse.setHeader("Cache-Control", "no-cache");

    httpResponse.setStatus(httpStatus);

    String json = "{\"code\":\"" + code + "\",\"desc\":\"" + desc + "\"}";
    httpResponse.getWriter().write(json);
  }

  private boolean isAjaxRequest(HttpServletRequest httpRequest) {
    return httpRequest.getHeader("x-requested-with") != null;
  }

  /**
   * 不同应用应具体实现.
   */
  protected abstract boolean checkSession(HttpSession session, HttpServletRequest request,
      HttpServletResponse response);

  protected abstract boolean checkPrivilege(HttpSession session, HttpServletRequest request,
      HttpServletResponse response);

  private boolean isExcludable(String servletPath) {

    if (failGo.equals(servletPath)) {
      return true;
    }

    if (excludePaths != null && excludePaths.length > 0) {
      for (String excludePath : excludePaths) {
        if (servletPath.contains(excludePath)) {
          return true;
        }
      }
    }
    if (excludePathRegs != null && excludePathRegs.length > 0) {
      for (String excludePathReg : excludePathRegs) {
        Pattern pattern = Pattern.compile(excludePathReg);
        Matcher matcher = pattern.matcher(servletPath);
        if (matcher.find()) {
          return true;
        }
      }
    }
    return false;
  }

  public void destroy() {
    // TODO Auto-generated method stub
  }
}
